/**
 * @file factorylib.c
 * @brief Factory for library (sdlib: Select Dynamic Library).
 *
 * Sorgente creato per permettere l'utilizzo del caricamento delle librerie dinamiche
 *  in maniera centralizzata e seguendo il design pattern factory
 * Responsabilità:
 *  - tenere in memoria tutte le librerie caricate per non caricarne duplicati inutilmente;
 *  - gestire il caricamento, supportando il client nella richiesta delle librerie e nella chiamata alle funzioni
 *  - individuare le informazioni sulle librerie
 *
 *
 * @date Feb 25, 2012
 * @author Francesco Corvino
 */
#include "factorylib.h"
#include "../logproxy/logproxy.h"

/**
 * @brief define utilizzato per individuare i log della libreria.
 */
#define SDLIB_IDENTIFY_NAME "SDLIB_FACTORY"

/**
 * Locazione dei puntatori che descrivono le librerie caricate in memoria
 * Uso privato
 */
DescrLib ** libBuffer = NULL;

/**
 * @brief Specifica come confrontare due entry di librerie dinamiche.
 */
int _SDLib_cmp(const void * a, const void * b){
	DescrLib *A, *B;
	if((a==NULL) && (b==NULL)) return 0;
	if(a!=NULL) A = (DescrLib*) a; else return -1;
	if(b!=NULL) B = (DescrLib*) b; else return 1;
	return strcmp(A->name, B->name);
}

/**
 * @brief Specifica come confrontare due entry di funzione per una libreria dinamica.
 */
int _SDLib_cmpFn(const void * a, const void *b){
	DescrFun *A, *B;
	if((a==NULL) && (b==NULL)) return 0;
	if(a!=NULL) A = (DescrFun*) a; else return -1;
	if(b!=NULL) B = (DescrFun*) b; else return 1;
	return strcmp(A->name, B->name);
}

/**
 * @brief Svuota la memoria occupata dalla libreria.
 *
 */
void _SDLib_destroy(const void * lib){
	if(lib==NULL) return;
	DescrLib* delLib = (DescrLib*) lib;
	// libera la memoria delle stringhe descrittive
	free(delLib->name);
	free(delLib->path);
	// elimina le informazioni sulle librerie presenti.
	DynArray_destroy(&(delLib->functions));
	// libera lo spazio di memoria della libreria
	closeLibrary(delLib->libpointer);
}

/**
 * @brief Permette di inizializzare i dati per un'array che conterrà tutte le librerie da caricare.
 */
void SDLIB_init(DynArray * libArr){
	DynArray_init(libArr,_SDLib_cmp,sizeof(DescrLib));
	libArr->destroy = _SDLib_destroy;
}

/**
 * @brief Prova a caricare una libreria
 */
DescrLib* SDLIB_create(char *name, char *path){
	DescrLib * lib = malloc(sizeof(DescrLib));
	if(lib!=NULL){
		DynArray_init(&(lib->functions),_SDLib_cmpFn,sizeof(DescrFun));
		if((lib->name = malloc(strlen(name)))!=NULL)
			memcpy(lib->name,name, strlen(name));
		if((lib->path = malloc(strlen(path)))!=NULL)
			memcpy(lib->path,path, strlen(path));
		lib->libpointer = loadLibrary(path);
		if(lib->libpointer == NULL) {
			log_namedlevel(SDLIB_IDENTIFY_NAME,LOG_ERROR_LEVEL ,"errore nella creazione");
			return lib;
		}
		// prova a caricare le informazioni sulla libreria
		HANDLER_FUN infoFn = getFunct(lib->libpointer,SDLIB_NAME_INTERFACEFUN);
		if(infoFn==NULL) {
			log_namedlevel(SDLIB_IDENTIFY_NAME,LOG_ERROR_LEVEL,
					"caricamento di una libreria senza definizione dell'interfaccia");
			return lib;
		}
		// richiede le informazioni sulla libreria
		int i = 0; SDLIBFunDef * res;
		do {
			SDLIBFunDef* (*exFun)(int) = (SDLIBFunDef* (*)(int)) infoFn;
			res = (*exFun)(i);
			if(res!=NULL) {
				DescrFun addEl;
				addEl.name = res->name; addEl.type = res->type;
				addEl.funpointer = getFunct(lib,addEl.name);
				DynArray_add(&(lib->functions),&addEl);
			}
			i++;
		} while(res!=NULL);
	}
	return lib;
}
